﻿using JWT.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.IdentityModel.Tokens.Jwt;

namespace BunnyEater.WebAPI.Attributes
{
    /// <summary>
    /// 自定义授权过滤器，用于JWT令牌验证
    /// </summary>
    public class MyAuthorizationed : Attribute, IAuthorizationFilter
    {
        /// <summary>
        /// 授权验证方法
        /// 判断是否允许匿名访问
        /// 401请求要求用户的身份认证
        /// </summary>
        /// <param name="context">授权过滤器上下文</param>
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var endpoint = context.HttpContext.Features.Get<IEndpointFeature>()?.Endpoint;
            if (endpoint != null && endpoint.Metadata.GetMetadata<AllowAnonymousAttribute>() != null) return;

            var authHeader = from t in context.HttpContext.Request.Headers where t.Key == "Authorization" || t.Key == "auth" select t.Value.FirstOrDefault();

            string token = authHeader.FirstOrDefault();
            if (!string.IsNullOrEmpty(token))
            {
                try
                {
                    token = token.Replace("auth ", string.Empty);
                    var readToken = new JwtSecurityTokenHandler().ReadJwtToken(token);
                    var ticks = DateTimeOffset.FromUnixTimeSeconds((long)readToken.Payload.Exp).Ticks;
                    TimeSpan ts = new TimeSpan(DateTime.Now.Ticks - ticks);
                    if (ts.TotalSeconds > 0)
                    {
                        context.Result = new StatusCodeResult(403);
                    }

                    //方式二：token过期触发 TokenExpiredException 异常(状态码403)
                    //var decoder = JwtHelper.GetJwtDecoder();
                    //var secret = JwtSecret.SecurityKey;
                    //var user = decoder.DecodeToObject<User>(token, secret, verify: true);
                }
                catch (SignatureVerificationException ex)
                {
                    context.Result = new StatusCodeResult(405);
                }
                catch (Exception ex)
                {
                    context.Result = new StatusCodeResult(404);
                }
                return;
            }
            context.Result = new StatusCodeResult(401);
        }
    }
}
